const path = require('path') // nodeJS 核心模块，处理路径
const os = require('os') // nodeJS 核心模块，处理系统信息
const ESLintPlugin = require('eslint-webpack-plugin') // ESLint 插件
const HtmlWebpackPlugin = require('html-webpack-plugin'); // HTML 打包插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin') // CSS 单独打包插件(使用 link 引入 CSS，避免使用 style 标签实现样式从而导致的卡顿)
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin') // CSS 压缩插件
const TerserPlugin = require('terser-webpack-plugin') // JavaScript 代码压缩插件（生产模式该插件自动开启）
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin"); // 图片压缩插件

const threads = os.cpus().length // 获取 cpu 的核心数

const getStyleLoaders = (preProcessor) => {
    return [  // 设置多个 loader
        MiniCssExtractPlugin.loader, // MiniCssExtractPlugin.loader 使用该插件d的 loader 将css 转换为的 js，单独打包成 CSS 输出 （style-loader：将 js 中 css 通过创建 style 标签的方式添加到 html 文件中生效）
        "css-loader", // 将 css 资源编译成 commonJS 的模块到 js 中
        {
            loader: "postcss-loader",  // 用以解决样式兼容性问题，需要加载 css-loader 之后，less-loader、sass-loader、styl-loader 之前
            options: {
                postcssOptions: {
                    plugins: [
                        "postcss-preset-env" // 使用预设
                    ]
                }
            }
        },
        preProcessor
    ].filter(Boolean) // 去除掉 undefined 项
}

module.exports = {
    // Webpack 打包入口
    entry: './src/main.js', // 相对路径，相对于命令执行处的路径，不需要改变
    // Webpack 打包输出位置
    output: {
        // 文件输出路径
        // __dirname nodeJS 变量，代表当前文件所在文件夹路径
        path: path.resolve(__dirname, '../dist'), // 绝对路径 path.resolve(a, b) 拼接路径： ab *** 注意：此文件在文件夹内部，__dirname 为文件夹路径，需要往外面退一个层级 ***
        // 入口文件输出的文件名
        filename: 'static/js/[name].js',
        // 动态导入输出资源命名方式
        chunkFilename: 'static/js/[name].chunk.js', 
        // 图片、字体等通过 type: asset 处理资源的命名方式
        assetModuleFilename: 'static/media/[name][ext][query]',
        // 自动清空上次打爆包的内容
        // 原理：在打包前，将 path 整个目录内容清空，再进行打包
        clean: true
    },
    // 加载器
    module: {
        rules: [ // 打包时每个文件都会经过所有 loader 处理
            {
                oneOf: [ // 只能匹配上一个 loader, 剩下的就不匹配了。
                    // 加载器配置
                    {
                        test: /\.css$/, // 匹配以 .css 结尾的文件
                        //loader: 'css-loader', // 只能设置单个 loader
                        use: getStyleLoaders()
                        // [  // 设置多个 loader
                        //     MiniCssExtractPlugin.loader, // MiniCssExtractPlugin.loader 使用该插件d的 loader 将css 转换为的 js，单独打包成 CSS 输出 （style-loader：将 js 中 css 通过创建 style 标签的方式添加到 html 文件中生效）
                        //     "css-loader", // 将 css 资源编译成 commonJS 的模块到 js 中
                        //     {
                        //         loader: "postcss-loader",  // 用以解决样式兼容性问题，需要加载 css-loader 之后，less-loader、sass-loader、styl-loader 之前
                        //         options: {
                        //             postcssOptions: {
                        //                 plugins: [
                        //                     "postcss-preset-env" // 使用预设
                        //                 ]
                        //             }
                        //         }
                        //     },
                        //     preProcessor
                        // ] loader 自下而上加载
                    },
                    {
                        test: /\.less$/,
                        use: getStyleLoaders('less-loader')
                        // [
                        //     MiniCssExtractPlugin.loader,
                        //     'css-loader',
                        //     {
                        //         loader: "postcss-loader",  // 用以解决样式兼容性问题，需要加载 css-loader 之后，less-loader、sass-loader、styl-loader 之前
                        //         options: {
                        //             postcssOptions: {
                        //                 plugins: [
                        //                     "postcss-preset-env" // 使用预设
                        //                 ]
                        //             }
                        //         }
                        //     },
                        //     'less-loader', // compiles Less to CSS
                        // ],
                    },
                    {
                        test: /\.s[ac]ss$/,
                        use: getStyleLoaders('sass-loader')
                        // [
                        //     MiniCssExtractPlugin.loader,
                        //     'css-loader',
                        //     {
                        //         loader: "postcss-loader",  // 用以解决样式兼容性问题，需要加载 css-loader 之后，less-loader、sass-loader、styl-loader 之前
                        //         options: {
                        //             postcssOptions: {
                        //                 plugins: [
                        //                     "postcss-preset-env" // 使用预设
                        //                 ]
                        //             }
                        //         }
                        //     },
                        //     'sass-loader', // 将 Sass 编译成 CSS
                        // ],
                    },
                    {
                        test: /\.styl$/,
                        use: getStyleLoaders('stylus-loader')
                        // [
                        //     MiniCssExtractPlugin.loader,
                        //     'css-loader',
                        //     {
                        //         loader: "postcss-loader",  // 用以解决样式兼容性问题，需要加载 css-loader 之后，less-loader、sass-loader、styl-loader 之前
                        //         options: {
                        //             postcssOptions: {
                        //                 plugins: [
                        //                     "postcss-preset-env" // 使用预设
                        //                 ]
                        //             }
                        //         }
                        //     },
                        //     'stylus-loader', // 将 Stylus 编译成 CSS
                        // ],
                    },
                    {
                        test: /\.(png|jpe?g|gif|webp)$/,  // 处理图片
                        type: 'asset',
                        parser: {
                            dataUrlCondition: {
                                maxSize: 10 * 1024 // 小于 10kb 的图片会被解析成 base64 放在 url 中。 优点：减少请求数量 缺点：文件体积会变大
                            }
                        },
                        // generator: {
                        //     // 将图片文件输出到 static/imgs 目录中
                        //     // 将图片文件命名 [hash:8][ext][query]
                        //     // [hash:8]: hash值取8位
                        //     // [ext]: 使用之前的文件扩展名
                        //     // [query]: 添加之前的query参数
                        //     filename: "static/imgs/[hash:8][ext][query]",
                        // }
                    },
                    {
                        test: /\.(ttf|woff2?|mp3|mp4|avi)$/,  // 处理图标 音视频等其他类型文件（不需要处理，只原封不动地输出）
                        type: 'asset/resource',
                        // generator: {
                        //     // 将图标文件输出到 static/media 目录中
                        //     // 将图标文件命名 [hash:8][ext][query]
                        //     // [hash:8]: hash值取8位
                        //     // [ext]: 使用之前的文件扩展名
                        //     // [query]: 添加之前的query参数
                        //     filename: "static/media/[hash:8][ext][query]",
                        // }
                    },
                    {
                        test: /\.m?js$/, // 处理 js
                        //exclude: /(node_modules|bower_components)/, // 排除 node_module 内的 js 代码 
                        include: path.resolve(__dirname, '../src'), // 仅打包 ../src 文件夹下的代码
                        use: [
                            {
                                loader: 'thread-loader', // 开启多进程编译
                                options: {
                                    workers: threads // 进程数
                                }
                            }, {
                                loader: 'babel-loader', // 使用 babel 编译器
                                options: { //可使用 options 指定 babel 规则，也可以不写，通过配置文件设置 babel 规则
                                    //presets: ['@babel/preset-env'] // 使用预设
                                    cacheDirectory: true, // 打开 babel 编译缓存，使用缓存避免未改动代码重复编译，从而提升打包速度
                                    cacheCompression: false, // 缓存压缩，不压缩以提高打包速度
                                    plugins: ['@babel/plugin-transform-runtime'] // 禁用 bable 对每个文件的垫片功能代码注入，所有使用的辅助代码片段从 @babel/plugin-transform-runtime 中直接引用，统一辅助代码片段在不同的文件中重复定义
                                }
                            }
                        ]
                    }
                ]
            }
        ]
    },
    // 插件
    plugins: [
        new ESLintPlugin({  // ESLint 语法检查插件
            context: path.resolve(__dirname, '../src'),
            exclude: 'node_modules', // 不去检查 node_modules 里的代码，默认配置，可不写
            cache: true, // 打开 Lint 缓存，对未修改的代码使用缓存中的检查结果，提升打包速度
            cacheLocation: path.resolve(__dirname, '../node_modules/.cache/.eslintcahce'), // 缓存文件存放目录
            threads // 开启多进程检查
        }),
        new HtmlWebpackPlugin({ // 自动生成 HTML 资源，并且引入打包的 js
            template: path.resolve(__dirname, '../public/index.html') // 使用指定路径的 HTML 文件作为模板，生成的 HTML 资源跟模板相同，并且引入了打包的 js 资源
        }),
        new MiniCssExtractPlugin({
            filename: 'static/css/[name].css', // 指定单独打包的 CSS 存放目录
            chunkFilename: 'static/css/[name].chunk.css'
        }),
        // new CssMinimizerPlugin(), // 压缩打包的 CSS 文件  -- webpack4 写法
        // new TerserPlugin({
        //     parallel: threads // 开启多线程压缩（生产模式会自动开启，但是如果需要额外配置需要重写）-- webpack4 写法
        // })
    ],
    optimization: { // webpack5 中，优化项配置在此处
        minimize: true,
        minimizer: [
            new CssMinimizerPlugin(), // 压缩打包的 CSS 文件 -- webpack5 写法
            new TerserPlugin({ // JavaScript 代码压缩
                parallel: threads // 开启多线程压缩（生产模式会自动开启，但是如果需要额外配置需要重写） -- webpack5 写法
            }),
            // 压缩图片
            new ImageMinimizerPlugin({
                minimizer: {
                    implementation: ImageMinimizerPlugin.imageminGenerate,
                    options: {
                        plugins: [
                            ["gifsicle", { interlaced: true }],
                            ["jpegtran", { progressive: true }],
                            ["optipng", { optimizationLevel: 5 }],
                            [
                                "svgo",
                                {
                                    plugins: [
                                        "preset-default",
                                        "prefixIds",
                                        {
                                            name: "sortAttrs",
                                            params: {
                                                xmlnsOrder: "alphabetical",
                                            },
                                        },
                                    ],
                                },
                            ],
                        ],
                    },
                },
            }),
        ],
        splitChunks: {
            chunks: 'all'
        }
    },
    // 模式：
    // -- development: 1.打包代码
    // -- production: 1.打包代码 2.压缩代码
    mode: 'production',
    // *** 生产模式不需要开启 devServer，故不会对 devServer 进行配置 ***
    devtool: 'source-map' //生成编译后代码和源代码的映射文件(*.map)，报错时可以定位到源文件的行列映射，编译速度慢
}